package main.fat;

import main.disk.Disk;
import main.disk.DiskException;

import java.util.Arrays;

/**
 * 文件分配表(FAT)管理类
 * 管理磁盘块的分配状态和链接关系
 */
public class FAT {
    // FAT常量
    public static final byte FREE = 0;          // 空闲块
    public static final byte EOF = (byte) 255;   // 文件结束标志
    public static final byte BAD_BLOCK = (byte) 254; // 坏块标志

    private final Disk disk;                    // 磁盘引用
    private byte[] fatTable;                    // FAT表(内存中)
    private boolean modified;                   // FAT表是否已修改

    /**
     * 构造函数：从磁盘加载FAT或创建新FAT
     * @param disk 磁盘实例
     * @param createNew 是否创建新的FAT
     * @throws DiskException 磁盘操作异常
     */
    public FAT(Disk disk, boolean createNew) throws DiskException {
        this.disk = disk;
        this.fatTable = new byte[Disk.DISK_SIZE];

        if (createNew) {
            initializeNewFAT();
        } else {
            loadFATFromDisk();
        }
    }

    /**
     * 初始化新的FAT表
     * @throws DiskException 磁盘操作异常
     */
    private void initializeNewFAT() throws DiskException {
        // 将所有块标记为空闲
        Arrays.fill(fatTable, FREE);

        // 标记系统区域(前2块)为已使用
        for (int i = 0; i < Disk.FAT_BLOCKS; i++) {
            fatTable[i] = BAD_BLOCK; // FAT区域本身标记为"坏块"，实际不可分配
        }

        // 标记根目录块为已使用(假设根目录占用1个块)
        fatTable[Disk.ROOT_DIR_BLOCK] = EOF;

        // 可以在这里标记其他坏块，例如:
        // fatTable[23] = BAD_BLOCK; // 第23块损坏
        // fatTable[49] = BAD_BLOCK; // 第49块损坏

        modified = true;
        saveFATToDisk(); // 立即保存到磁盘
    }

    /**
     * 从磁盘加载FAT表
     * @throws DiskException 磁盘操作异常
     */
    private void loadFATFromDisk() throws DiskException {
        byte[] buffer = new byte[Disk.BLOCK_SIZE];

        // 读取FAT占用的所有块
        for (int blockNum = 0; blockNum < Disk.FAT_BLOCKS; blockNum++) {
            disk.readBlock(blockNum, buffer);

            // 计算当前块在fatTable中的偏移量
            int offset = blockNum * Disk.BLOCK_SIZE;
            int bytesToCopy = Math.min(Disk.BLOCK_SIZE, Disk.DISK_SIZE - offset);

            // 将数据从缓冲区复制到fatTable
            System.arraycopy(buffer, 0, fatTable, offset, bytesToCopy);
        }

        modified = false;
    }

    /**
     * 将FAT表保存到磁盘
     * @throws DiskException 磁盘操作异常
     */
    public void saveFATToDisk() throws DiskException {
        if (!modified) {
            return; // 未修改，无需保存
        }

        byte[] buffer = new byte[Disk.BLOCK_SIZE];

        for (int blockNum = 0; blockNum < Disk.FAT_BLOCKS; blockNum++) {
            // 计算当前块在fatTable中的偏移量
            int offset = blockNum * Disk.BLOCK_SIZE;
            int bytesToCopy = Math.min(Disk.BLOCK_SIZE, Disk.DISK_SIZE - offset);

            // 清空缓冲区
            Arrays.fill(buffer, (byte) 0);

            // 将fatTable中的数据复制到缓冲区
            System.arraycopy(fatTable, offset, buffer, 0, bytesToCopy);

            // 写入磁盘块
            disk.writeBlock(blockNum, buffer);
        }

        modified = false;
    }

    /**
     * 分配一个空闲磁盘块
     * @return 分配到的块号，若无空闲块返回-1
     */
    public int allocateBlock() {
        // 从系统区域之后开始查找空闲块
        for (int i = Disk.FAT_BLOCKS; i < Disk.DISK_SIZE; i++) {
            if (fatTable[i] == FREE) {
                fatTable[i] = EOF; // 初始化为文件结束
                modified = true;
                return i;
            }
        }
        return -1; // 无空闲块
    }

    /**
     * 释放一个磁盘块
     * @param blockNum 要释放的块号
     */
    public void freeBlock(int blockNum) {
        if (blockNum >= Disk.FAT_BLOCKS && blockNum < Disk.DISK_SIZE) {
            fatTable[blockNum] = FREE;
            modified = true;
        }
    }

    /**
     * 获取指定块的下一个块号
     * @param currentBlock 当前块号
     * @return 下一个块号，如果是文件结束返回-1
     */
    public int getNextBlock(int currentBlock) {
        if (currentBlock < 0 || currentBlock >= Disk.DISK_SIZE) {
            return -1;
        }

        int nextBlock = fatTable[currentBlock] & 0xFF; // 转换为无符号字节值

        if (nextBlock == EOF) {
            return -1; // 文件结束
        }

        return nextBlock;
    }

    /**
     * 设置当前块的下一个块号
     * @param currentBlock 当前块号
     * @param nextBlock 下一个块号
     */
    public void setNextBlock(int currentBlock, int nextBlock) {
        if (currentBlock >= 0 && currentBlock < Disk.DISK_SIZE &&
                nextBlock >= 0 && nextBlock < Disk.DISK_SIZE) {
            fatTable[currentBlock] = (byte) nextBlock;
            modified = true;
        }
    }

    /**
     * 获取FAT表项的值
     * @param blockNum 块号
     * @return FAT表项值
     */
    public byte getFatEntry(int blockNum) {
        if (blockNum < 0 || blockNum >= Disk.DISK_SIZE) {
            return BAD_BLOCK;
        }
        return fatTable[blockNum];
    }

    /**
     * 检查指定块是否空闲
     * @param blockNum 块号
     * @return 是否空闲
     */
    public boolean isBlockFree(int blockNum) {
        return blockNum >= 0 && blockNum < Disk.DISK_SIZE && fatTable[blockNum] == FREE;
    }

    /**
     * 获取空闲块数量
     * @return 空闲块数量
     */
    public int getFreeBlockCount() {
        int count = 0;
        for (int i = Disk.FAT_BLOCKS; i < Disk.DISK_SIZE; i++) {
            if (fatTable[i] == FREE) {
                count++;
            }
        }
        return count;
    }
}